#!/usr/bin/python
# -*- coding:utf-8 -*-
#package_checker.py
# lingyue.wkl@163.com or wukunliang@163.com
#This script is used to check the rpm package.
#Just insure the info of rpm follows the principles of hadoop job testing!

#created 2011-10-17 version 0.1
#added base info checke 2011-10-18 version 0.2
#added colored output message  2011-10-21 version 0.3   need to rebuild all the functions
#added the const used to config and rebuild all the functions,from line 280 to  < 200  2011-10-28 version0.4
#added illegal files check make install path configurable 2011-12-04 version 0.5
#added spec check. modify the pass/failed show color. 2011-12-11 version0.6
#rebuild all functions,local version ,just support algo testing. 2012-03-09 version0.7

#待解决问题 
  #svn export 权限问题
  #配置挪到xml或property文件

import os,rpm,sys,getopt,glob

#-----------------------------------------------
#可配变量

#1 需检查是否为没用/公用的文件 文件长度，文件名所带关键字 标黄显示，需确认
CONST_UN_USE_FILENAME_LENGTH = 4
CONST_UN_USE_FILENAME_CONTAIN = ["test",".jar","log"]

#2 出现如下文件，标红显示，警告
CONST_ILLEGAL_FILENAME_CONTAIN = [".svn",".java",".cpp",".h"]

#3 合法的安装路径
CONST_LEGAL_INSTALL_PATH = ["/data/SCHED_DATA/", "/home/tscadmin/skynet_jobs/"]
CONST_LEGAL_RELEASE_TYPE = ["el5"]
CONST_INSTALL_USER = ["tscadmin"]

#4 你想要关注的在文件列表里的内容 文件名所包含关键字
CONST_CONCERN_FILENAME_CONTAIN = ["conf"]

#5 是否开启检测svn
IS_CHECK_SVN = 1

#6 是否开启SPEC检测
IS_CHECK_SPEC = 1

#-----------------------------------------------
#在example模板文件的*.sh中可被替换掉的标记
CONST_REPLACE_RPM_TAG_NAME = "#RPM_TAG_NAME"
CONST_REPLACE_RPM_PROJECT_DIR = "#RPM_PROJECT_DIR"
CONST_REPLACE_RPM_PATH_FIRST = "#RPM_PATH_FIRST"
CONST_REPLACE_RPM_PATH_FOLLOW = "#RPM_PATH_FOLLOW"
CONST_REPLACE_MD5 = "#RPM_PATH_MD5"

COLOR_NONE = "\033[m"
COLOR_GREEN = "\033[01;32m"
COLOR_RED = "\033[01;31m"
COLOR_YELLOW = "\033[01;33m"

COLOR_PASS_STR = COLOR_GREEN + "PASS" + COLOR_NONE
COLOR_WARNING_STR = COLOR_RED + "FAILED" + COLOR_NONE

def get_hdr(rpm_path):
    ts = rpm.ts()
    try:
      fdno = os.open(rpm_path, os.O_RDONLY)
      hdr = ts.hdrFromFdno(fdno)
      os.close(fdno)
    except:
      print("ERROR: Init rpm error!")
      print(get_color_str(COLOR_RED, "There is something happend to the rpm package.Can't read and get Info,Try download again!"))
      sys.exit(1)

    return hdr

def get_rpm_info(file_path):
    rpm_info = {}
    hdr = get_hdr(file_path)
    tag_files = hdr[rpm.RPMTAG_FILENAMES] 

    rpm_info.update({"tag_name":str(hdr[rpm.RPMTAG_NAME])})
    rpm_info.update({"tag_version":str(hdr[rpm.RPMTAG_VERSION])})
    rpm_info.update({"tag_release":str(hdr[rpm.RPMTAG_R])})
    rpm_info.update({"tag_vendor":str(hdr[rpm.RPMTAG_VENDOR])})
    rpm_info.update({"tag_desc":str(hdr[rpm.RPMTAG_SUMMARY])})
    rpm_info.update({"tag_packager":str(hdr[rpm.RPMTAG_FILEUSERNAME][0])})
    rpm_info.update({"tag_files":sorted(tag_files)})
    rpm_info.update({"tag_xml_files":[ s for s in hdr[rpm.RPMTAG_FILENAMES] if ".xml" in s ]})
    rpm_info.update({"tag_install_path":os.path.commonprefix(tag_files)})

    com_pre_len = len(os.path.commonprefix(tag_files).split(os.sep))
    rpm_info.update({"tag_install_dir_depth":max([len(s.split(os.sep)) for s in tag_files if ".svn" not in s ])-com_pre_len})

    for s in str(hdr[rpm.RPMTAG_DESCRIPTION]).split("\n"):
      if s.startswith("http://"):
        rpm_info.update({"tag_svn_info":s})
        break
    else :
      rpm_info.update({"tag_svn_info":""})
    return rpm_info

def sed_const_param(tag_name,pro_dir,rpm_url, dir_depth):
    files_to_sed = glob.glob("*.sh")
    files_to_sed.extend(glob.glob("*/*.sh"))

    sum_prefix = "#md5sum $dir_pro/*"
    sum_bak_prefix = "#md5sum $dir_pro.bak/*"
    sed_mid = "|sed 's/.bak//g'"
    md5sum_str = sum_prefix + " > new\n" + sum_bak_prefix + " " + sed_mid + " > old\n"
    for i in range(1,dir_depth):
      sum_prefix += "/*"
      sum_bak_prefix += "/*"
      md5sum_str += sum_prefix + " >> new\n" + sum_bak_prefix + " " + sed_mid + " >> old\n"

    for sh_f in files_to_sed:
      f = open(sh_f,"r")
      content = f.read()
      f.close()

      content = content.replace(CONST_REPLACE_RPM_TAG_NAME,tag_name)
      content = content.replace(CONST_REPLACE_RPM_PROJECT_DIR,pro_dir)
      content = content.replace(CONST_REPLACE_RPM_PATH_FIRST,rpm_url)
      content = content.replace(CONST_REPLACE_RPM_PATH_FOLLOW,rpm_url)
      content = content.replace(CONST_REPLACE_MD5,md5sum_str)

      f = open(sh_f,"w")
      f.write(content)
      f.close()

def get_color_str(color,msg):
    return color + msg + COLOR_NONE

def print_info(msg):
    return COLOR_YELLOW + msg + COLOR_NONE

def print_conf(msg):
    return COLOR_GREEN + msg + COLOR_NONE

def print_error(msg):
    return COLOR_RED + msg + COLOR_NONE 

def print_base(key,content,status):
    content = ("[" + key + "] ").ljust(20) + content.ljust(50) + status.ljust(20)
    if status == COLOR_PASS_STR:
      content = COLOR_GREEN + content + COLOR_NONE
    elif status == COLOR_WARNING_STR:
      content = COLOR_RED + content + COLOR_NONE
    print(content)

def is_no_used_or_concern(list, file_name):
    for con in list:
      if con in file_name.lower():
        return True
    return False

def print_dir_info(file_list):
    for f in file_list:
      if "." in f.split(os.sep)[-1] and ".svn" not in f.split(os.sep)[-1]:
        file_name = os.path.basename(f)
        if is_no_used_or_concern(CONST_CONCERN_FILENAME_CONTAIN, file_name):
          print("- " + os.path.dirname(f) + "/" + print_conf(file_name))
        elif len(file_name) <= CONST_UN_USE_FILENAME_LENGTH or is_no_used_or_concern(CONST_UN_USE_FILENAME_CONTAIN, file_name):
          print("- " + print_info(f))
        elif is_no_used_or_concern(CONST_ILLEGAL_FILENAME_CONTAIN, file_name):
          print("- " + print_error(f))
        else:
          print("- " + f)
      else:
        if ".svn" == f.split(os.sep)[-1] and IS_CHECK_SVN != 0:
          print("d " + print_error(f))
        elif ".svn" in f:
          continue
        else:
          print("d " + f)

def print_check_info(rpm_info):
    tag_project_name = rpm_info["tag_name"]
    tag_version = rpm_info["tag_version"]
    tag_release = rpm_info["tag_release"]
    tag_vendor = rpm_info["tag_vendor"]
    tag_dsec = rpm_info["tag_desc"]
    tag_packager = rpm_info["tag_packager"]
    tag_file_count = str(len(rpm_info["tag_files"]))
    tag_xml_file_list = rpm_info["tag_xml_files"]
    tag_install_path = rpm_info["tag_install_path"]
    tag_svn_info = rpm_info["tag_svn_info"]

    #Print header
    print_base("project name", tag_project_name, '')
    print_base("version", tag_version, '')

    release_legal = False
    for release_type in CONST_LEGAL_RELEASE_TYPE:
      if release_type in tag_release:
        release_legal = True
    if not release_legal:
      ERROR_INFO.append(1)
      print_base("release", tag_release, COLOR_WARNING_STR)
    else:
      print_base("release", tag_release, COLOR_PASS_STR)

    print_base("vendor", tag_vendor, '')
    print_base("svn_path", tag_svn_info.split("\n")[0], '')
    print_base("desc", tag_dsec, '')

    if tag_packager.strip() not in CONST_INSTALL_USER:
      ERROR_INFO.append(2)
      print_base("owner", tag_packager, COLOR_WARNING_STR)
    else:
      print_base("owner", tag_packager, COLOR_PASS_STR)

    print_base("file count", tag_file_count, '')
    
    install_path_legal = False
    for install_path in CONST_LEGAL_INSTALL_PATH:
      if tag_install_path.strip().startswith(install_path):
        install_path_legal = True

    if install_path_legal:
      print_base("install path", tag_install_path, COLOR_PASS_STR)
    else:  
      ERROR_INFO.append(3)
      print_base("install path", tag_install_path, COLOR_WARNING_STR)

    xml_file_count = len(tag_xml_file_list)
    if xml_file_count > 0:
      print_base("xml file count", str(xml_file_count), COLOR_PASS_STR)
      print_base("xml file list", "", print_info("check content yourself"))
      for xml_file_path in tag_xml_file_list:
         print_base("", xml_file_path, "")
    else:
      ERROR_INFO.append(4)
      print_base("xml file count", str(xml_file_count), COLOR_WARNING_STR)

def main():
    global ERROR_INFO
    ERROR_INFO = []
    try:
        opts,args = getopt.getopt(sys.argv[1:], "i:o:hs")

        for op,value in opts:
          if op in ("-h", "-H", "--help"):
            print("COMMAND: /home/algoqa/rpm_check/rpm_check.py -i rpm_url/local_rpm_path") 
            sys.exit(0)
          if op == "-i":
            inpath = value
          elif op == "-o":
            outpath = value
          elif op == "-s":
            is_sed = True
    except getopt.GetoptError:
      print(sys.argv[0] + " : params are not defined well!")
   
    if 'is_sed' not in dir():
      is_sed = False
    
    rpm_url = inpath
    rpm_file_name = os.path.split(rpm_url)[1]
    check_info = []

    if os.path.exists("./"+rpm_file_name):
      check_info.append("Version not update,use the old one!")
    else:
      check_info.append("remove old rpm package!")
      for rpm_f in glob.glob("*.rpm"):
         os.remove(rpm_f)
      check_info.append("get new rpm package!")

      os.system('wget ' + rpm_url + " -P ./")
    check_info.append("check begin!")
    #print(check_info)
    rpm_info = get_rpm_info(rpm_file_name)

    if is_sed:
      sed_const_param(rpm_info["tag_name"],rpm_info["tag_install_path"],rpm_url, rpm_info["tag_install_dir_depth"])

    print ("="*50+" PACKAGE CHECKER "+"="*50).center(110)

    print("[BASE-INFO]")

    print_check_info(rpm_info)

    if len(ERROR_INFO) > 0:
      print("\n[ERROR-INFO]")
      index = 1
      if 1 in ERROR_INFO:
        print(str(index) + ". " + print_error("Relase type should be " + str(CONST_LEGAL_RELEASE_TYPE)))
        index += 1
      if 2 in ERROR_INFO:
        print(str(index) + ". " + print_error("Auth should be " + str(CONST_INSTALL_USER)))
        index += 1
      if 3 in ERROR_INFO:
        print(str(index) + ". " + print_error("Install path should be " + str(CONST_LEGAL_INSTALL_PATH)))
        index += 1
      if 4 in ERROR_INFO:
        print(str(index) + ". " + print_error("There should be at least one skynet_conf.xml"))
        index += 1
    print("")

    print("[DIR-INFO]\n" + get_color_str(COLOR_RED,"ERROR ") + get_color_str(COLOR_YELLOW,"CONFIRM ") + get_color_str(COLOR_GREEN,"CONCERN"))
    print_dir_info(rpm_info["tag_files"])

    #如果查询得到spec_check.py才进行检测
    #否则即使配置开启spec检测也不进行检测
    try:
      import spec_check
      spec_model = True
    except:
      spec_model = False

    print ("\n"+"="*50+" SPEC CHECKER "+"="*50).center(110)
    if IS_CHECK_SPEC != 0 and spec_model:
      svn_path = rpm_info["tag_svn_info"]+"/"+rpm_info["tag_name"]+".spec"
      spec_check.do_check(svn_path)

if __name__ =="__main__":
   main()
